package com.terry.trace.core;

import com.terry.trace.core.constant.DatePatternConstant;
import com.terry.trace.core.util.DateUtils;
import com.terry.trace.core.util.IdGenerator;
import com.terry.trace.core.util.InetAddressUtils;
import com.terry.trace.core.util.StringUtil;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;

import java.net.InetAddress;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 日志标签工具
 * @author TerryJiang
 */
public class LogTagTool {

    /**
     * ip 在第一次设值后存储在变量中，不需要频繁设值
     */
    private static String IP = null;
    /**
     * 主机名 在第一次设值后存储在变量中，不需要频繁设值
     */
    private static String HOST_NAME = null;

    public static final String LOG_TAG_FLOW_NO = "flowNo";
    private static final String LOG_TAG_TRACE_ID = "traceId";

    /** 行号 **/
    private static final String LOG_TAG_LINE_NUMBER = "lineNum";
    private static final String LOG_TAG_SERVICE_NAME = "service";
    private static final String LOG_TAG_FUNCTION_NAME = "funcName";

    private static final String LOG_TAG_TIMESTAMP = "time";
    private static final String LOG_TAG_IP = "ip";
    private static final String LOG_TAG_HOST_NAME = "hostName";

    /**
     * 获取堆栈中的类的简称
     * @param stackTraceElement
     * @return
     */
    public static String getStackTraceElementClassSimpleName(StackTraceElement stackTraceElement) {
        return stackTraceElement.getClassName().substring(stackTraceElement.getClassName().lastIndexOf(".") + 1);
    }

    /**
     * 获取全局logTag，不会存在null的返回值
     * @return
     */
    private static Map<String, String> getNotNullContextLogTag() {
        Map<String, String> logTags = LogTagContext.get();
        if(logTags == null) {
            logTags = new LinkedHashMap<>();
            LogTagContext.set(logTags);
        }

        if (null == IP || null == HOST_NAME) {
            // 获取inetAddress有一定性能消耗，不要频繁获取
            InetAddress inetAddress = InetAddressUtils.getLocalHostLANAddress();
            if (inetAddress != null) {
                // 在第一次设值后存储在变量中，不需要频繁设值
                IP = inetAddress.getHostAddress();
                HOST_NAME = inetAddress.getHostName();
            }
        }
        logTags.put(LogTagTool.LOG_TAG_IP, IP);
        logTags.put(LogTagTool.LOG_TAG_HOST_NAME, HOST_NAME);

        return logTags;
    }

    /**
     * 设置flowNo
     * @param value
     */
    public static void putFlowNo(String value) {
        Map<String, String> logTags = getNotNullContextLogTag();
        logTags.put(LogTagTool.LOG_TAG_FLOW_NO, value);
    }

    /**
     * 设置logTag键值对
     * @param key
     * @param value
     */
    public static void putLogTag(String key, String value) {
        Map<String, String> logTags = getNotNullContextLogTag();
        logTags.put(key, value);
    }

    /**
     * 获取上下文中的指定key对应的value
     * @param logTagsKey
     * @return
     */
    public static String getContextLogTagValue(String logTagsKey) {
        Map<String, String> logTags = LogTagContext.get();
        return logTags == null ? null : logTags.get(logTagsKey);
    }

    /**
     * 获取上下文中的flwoNo
     * @return
     */
    public  static String getContextFlowNo() {
        return getContextLogTagValue(LogTagTool.LOG_TAG_FLOW_NO);
    }

    /**
     * 移除上下文全局日志参数
     */
    public static void removeContextLogTag() {
        LogTagContext.remove();
    }

    /**
     * 通过内部的map生产log信息
     * @param stackTraceElement
     * @return
     */
    public static Map<String, String> genLogMsgWithInnerMap(StackTraceElement stackTraceElement) {
        String simpleClassName = getStackTraceElementClassSimpleName(stackTraceElement);
        String methodName = stackTraceElement.getMethodName();
        int lineNumber = stackTraceElement.getLineNumber();
        return genLogMsgWithInnerMapByClassMethod(simpleClassName, methodName, lineNumber);
    }

    /**
     * 通过内部的map生产log信息
     * @param className
     * @param methodName
     * @return
     */
    public static Map<String, String> genLogMsgWithInnerMapByClassMethod(String className, String methodName){
        return genLogMsgWithInnerMapByClassMethod(className, methodName, 0);
    }

    /**
     * 通过内部的map生产log信息
     * @param className
     * @param methodName
     * @param lineNumber
     * @return
     */
    public static Map<String, String> genLogMsgWithInnerMapByClassMethod(String className, String methodName, int lineNumber) {
        Map<String, String> logTags = getNotNullContextLogTag();
        // 如果上下文没有flowNo，则生成flowNo
        if(StringUtil.isTrimEmpty(logTags.get(LogTagTool.LOG_TAG_FLOW_NO))) {
            logTags.put(LogTagTool.LOG_TAG_FLOW_NO, IdGenerator.generateFlowNo());
        }
        logTags.put(LogTagTool.LOG_TAG_SERVICE_NAME, className);
        logTags.put(LogTagTool.LOG_TAG_FUNCTION_NAME, methodName);
        logTags.put(LOG_TAG_LINE_NUMBER, String.valueOf(lineNumber));
        logTags.put(LogTagTool.LOG_TAG_TIMESTAMP, DateUtils.format(new Date(), DatePatternConstant.NORM_DATETIME_PATTERN));

        // 加入SkyWalking traceId
        try {
            logTags.put(LogTagTool.LOG_TAG_TRACE_ID, TraceContext.traceId());
        } catch (Exception e) {
            logTags.put(LogTagTool.LOG_TAG_TRACE_ID, "");
        }

        return logTags;
    }
}
